#include "Rossenblatt.h"

#include <exception>
#include <iostream>
#include <memory>

#include "../../inc/tinyxml2.h"
#include "../Utils/StrUtil.h"
#include "../Math/TransferFunction/TransferFunctionBuilder.h"

using namespace std;
using namespace tinyxml2;

CRossenblatt::CRossenblatt(int InputDataCount, shared_ptr<ITransferFunctionBase> transferFunction) :
	m_InputDataDimesion(InputDataCount),
	m_Neuron(InputDataCount, transferFunction)
{
}

bool CRossenblatt::Save(const char * szFileName)
{
	auto vWeight = this->m_Neuron.GetWeightVector();
	auto dimessionCount = vWeight.GetDimesion() - 1;

	XMLDocument doc;

	doc.InsertEndChild(doc.NewDeclaration());

	auto rootElement = doc.InsertEndChild(doc.NewElement("ANNT"));

	auto networkElement = doc.NewElement("Network");

	networkElement->SetAttribute("TypeName", "Rossenblatt");
	networkElement->SetAttribute("SynapseCount", dimessionCount);

	auto weightListElement = doc.NewElement("InputWeightList");

	for (int i = 0; i < dimessionCount; i++)
	{
		auto w = vWeight.GetVectorElement(i);

		auto weightElement = doc.NewElement("Weight");
		weightElement->SetAttribute("Value", StringUtil::Double2Str(w).c_str());

		weightListElement->InsertEndChild(weightElement);
	}

	auto bias = vWeight.GetVectorElement(dimessionCount);

	auto biasWeightElement = doc.NewElement("BiasWeight");
	biasWeightElement->SetAttribute("Value", StringUtil::Double2Str(bias).c_str());

	auto transFuncElement = doc.NewElement("TransferFunction");

	auto transFuncObj = this->m_Neuron.GetTransferFunctionObject();
	auto funcProps = transFuncObj->Serialize();
	for (auto prop = funcProps.begin(); prop != funcProps.end(); ++prop)
	{
		transFuncElement->SetAttribute(prop->first.c_str(), prop->second.c_str());
	}

	networkElement->InsertEndChild(weightListElement);
	networkElement->InsertEndChild(biasWeightElement);
	networkElement->InsertEndChild(transFuncElement);

	rootElement->InsertEndChild(networkElement);

	auto saveRes = doc.SaveFile(szFileName);
	if (XML_SUCCESS == saveRes)
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool CRossenblatt::Load(const char * szFileName)
{
	XMLDocument doc;

	if (XML_SUCCESS != doc.LoadFile(szFileName))
	{
		return false;
	}

	auto rootElement = doc.RootElement();

	auto networkElement = rootElement->FirstChildElement("Network");

	auto networkTypeName = networkElement->Attribute("TypeName");
	if (::strcmp(networkTypeName, "Rossenblatt") != 0)
	{
		return false;
	}

	int dimessionCount = ::atoi(networkElement->Attribute("SynapseCount"));
	if (dimessionCount <= 0)
	{
		return false;
	}

	auto vW = this->m_Neuron.GetWeightVector();
	vW.ResetDimesion(dimessionCount + 1);

	auto weightListElement = networkElement->FirstChildElement("InputWeightList");
	if (weightListElement == nullptr)
	{
		return false;
	}

	auto currentWeightElement = weightListElement->FirstChildElement("Weight");
	if (currentWeightElement == nullptr)
	{
		return false;
	}

	for (int i = 0; i < dimessionCount; i++)
	{
		vW[i] = ::atof(currentWeightElement->Attribute("Value"));

		currentWeightElement = currentWeightElement->NextSiblingElement();
	}

	auto biasElement = networkElement->FirstChildElement("BiasWeight");
	if (biasElement == nullptr)
	{
		return false;
	}

	vW[dimessionCount] = ::atof(biasElement->Attribute("Value"));

	this->m_Neuron.SetWeightVector(vW);

	auto transferFunctionElement = networkElement->FirstChildElement("TransferFunction");
	if (transferFunctionElement == nullptr)
	{
		return false;
	}

	map<string, string> funcPropPairs;
	auto funcAttribute = transferFunctionElement->FirstAttribute();
	do
	{
		funcPropPairs[funcAttribute->Name()] = funcAttribute->Value();

		funcAttribute = funcAttribute->Next();
	} while (funcAttribute != nullptr);

	auto transferFuncObj = CTransferFunctionBuilder::Build(funcPropPairs);
	this->m_Neuron.ResetTransferFunctionObject(transferFuncObj);

	return true;
}

Vector CRossenblatt::Execute(Vector InputData) throw(exception)
{
	auto output = this->m_Neuron.Execute(InputData);

	Vector voutput(1);
	voutput[0] = output;

	return voutput;
}
